﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using DiscoveryLogic.Analytics.BusinessLogic.Entities;
using DiscoveryLogic.Analytics.BusinessLogic.Measures;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Measures;
using DiscoveryLogic.Analytics.DataObjects.Networks;
using DiscoveryLogic.Analytics.DataObjects.Rules;
using DiscoveryLogic.Analytics.DataObjects.Visuals;
using DiscoveryLogic.Analytics.Diagrams.NetworkGraphs;
using DiscoveryLogic.Analytics.Diagrams.Physics;
using DiscoveryLogic.Analytics.Diagrams.Physics.Renderers;
using DiscoveryLogic.Analytics.Modules;
using DiscoveryLogic.Analytics.Modules.Explorers;
using DiscoveryLogic.Analytics.Modules.Nodes;
using DiscoveryLogic.Analytics.UI.Common.Events;
using DiscoveryLogic.Common.Utilities;
using EntityBase=DiscoveryLogic.Analytics.DataObjects.EntityBase;

namespace DiscoveryLogic.Analytics.Diagrams.GravityGraphs
{
    /// <summary>
    /// Interaction logic for GravityGraphView.xaml
    /// </summary>
    public partial class GravityGraphView : UserControl
    {
        #region events
        public delegate void OnEntitySelected(EntityBase entity);

        public event OnEntitySelected EntitySelected;
        #endregion

        private GravityGraphPresenter _Presenter;

        public GravityGraphView()
        {
            InitializeComponent();
        }

        public GravityGraphView(GravityGraphPresenter presneter)
			: this()
		{
			_Presenter = presneter;
			_Presenter.View = this;
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            // this.InitTestGraphObjects();
        }

        public void InitTestGraphObjects(Network network)
        {
            NetworkVisual visual = NetworkVisual.Read(network.NetworkName);
            List<VerticeMeasure> verticeMeasures = NetworkMeasureManager.GetVerticeMeasures(network.NetworkName);
            Dictionary<EntityType, Dictionary<string, VerticeMeasure>> measures =
                NetworkMeasureManager.ToDictionary(verticeMeasures);
            VerticeMeasureRange range = NetworkMeasureManager.GetMeasureRanges(verticeMeasures);

            this.gravityGraph.Children.Clear();
            this.gravityGraph.ParticleSystem.Particles=new List<Particle>();
            // List<PersonDO> persons = EntityManager.GeneratePersonsForTest();
            int w = (int)this.gravityGraph.ActualWidth;
            int h = (int)this.gravityGraph.ActualHeight;
            Random randGen=new Random(DateTime.Now.Second);
            foreach (EntityBase entity in network.Records)
            {
                Point pos=new Point(this.RandomDistance(randGen, 10, w), this.RandomDistance(randGen, 10, h));
                Particle p = new Particle(1.0f, pos.ToVector(), entity);
                p.Control = this.CreateEntityControl(entity,visual,measures[entity.EntityType][entity.PKValue()],range);
                this.gravityGraph.ParticleSystem.Particles.Add(p);
            }
            Particle anchor = new Particle(
                double.PositiveInfinity,
                new Vector((double) w/2, 40), true);
            anchor.Control = this.CreateAnchor();
            this.gravityGraph.ParticleSystem.Particles.Add(anchor);
            this.GeneratePhysicsForParticles(this.gravityGraph.ParticleSystem.Particles,
                    anchor, false, 840.0f, 100.0, 100.0);

            this.gravityGraph.StartSimulation();
        }

        private double RandomDistance(Random randGen, int min, int max)
        {
            double distance = randGen.Next(max);
            while (distance < min)
                distance = randGen.Next(max);
            return distance;
        }

        private void GeneratePhysicsForParticles(List<Particle> particles,
           Particle anchor, bool createNewobjects, double restFirstToEnd,
           double restLengthToAnchor, double restLengthToEachOther)
        {
            try
            {
                //springs
                Spring[] anchorSprings = new Spring[particles.Count()];
                Spring[] particleSprings = new Spring[particles.Count() - 1];
                Spring springLastToFirst;

                //pens
                Pen transparentPen = new Pen(Brushes.Transparent, 1.0);
                Pen whiteSmokeDashPen = new Pen(Brushes.Gainsboro, 2.0);
                whiteSmokeDashPen.DashStyle = DashStyles.Dash;
                Pen whiteSmokeSolidPen = new Pen(Brushes.Gainsboro, 4.0);

                //renderers
                RubberBand transparentBandRenderer =
                    new RubberBand(transparentPen);
                RubberBand whiteSmokeDashBandRenderer =
                    new RubberBand(whiteSmokeDashPen);
                RubberBand whiteSmokeSolidRenderer =
                    new RubberBand(whiteSmokeSolidPen);

                //setup anchorSprings
                for (int i = 0; i < particles.Count; i++)
                {
                    //260.0f
                    anchorSprings[i] = new Spring(anchor, particles[i],
                        restLengthToAnchor, 1.5f, 2.0f);
                    anchorSprings[i].Renderer = whiteSmokeSolidRenderer;
                    this.gravityGraph.ParticleSystem.Springs.Add(anchorSprings[i]);
                }

                //setup particleSprings
                for (int i = 0; i < particleSprings.Length; i++)
                {
                    //60.0f
                    particleSprings[i] = new Spring(particles[i],
                        particles[i + 1], restLengthToEachOther, 3.5f, 2.0f);
                    particleSprings[i].Renderer = whiteSmokeDashBandRenderer;
                    this.gravityGraph.ParticleSystem.Springs.Add(particleSprings[i]);
                }

                //setup first to last very strong spring
                springLastToFirst = new Spring(particles[0],
                    particles[particles.Count - 1], restFirstToEnd, 3.5f, 2.0f);
                springLastToFirst.Renderer = transparentBandRenderer;
                this.gravityGraph.ParticleSystem.Springs.Add(springLastToFirst);

                if (createNewobjects)
                {
                    //particlesLatched = particles;
                    //anchorSpringsLatched = anchorSprings;
                    //particleSpringsLatched = particleSprings;
                    //springLastToFirstLatched = springLastToFirst;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                MessageBox.Show(ex.Message);
            }
        }

        private EntityControlBase CreateAnchor()
        {
            return new OrgNode(new ExternalOrgDO(), NetworkVisual.Default.EntityVisuals[EntityType.Organizations],
                false,new VerticeMeasure(), new VerticeMeasureRange());
        }

        private EntityControlBase CreateEntityControl(
            EntityBase entity, NetworkVisual visual, 
            VerticeMeasure measure, VerticeMeasureRange range)
        {
            EntityControlBase entityView = null;
            switch (entity.EntityType)
            {
                case EntityType.Persons:
                    entityView = new PersonNode(entity,
                        visual.EntityVisuals[EntityType.Persons],
                        false,measure,range);
                    ((PersonNode)entityView).ExploreSelectedEntity+=new DiscoveryLogic.Analytics.UI.Common.Events.OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    break;
                case EntityType.Publications:
                    entityView = new PubNode(entity,
                        visual.EntityVisuals[EntityType.Publications],
                        false,measure,range);
                    ((PubNode)entityView).ExploreSelectedEntity +=new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    break;
                case EntityType.Grants:
                    entityView = new GrantNode(
                        entity, visual.EntityVisuals[EntityType.Grants],
                        false, measure, range);
                    ((GrantNode)entityView).ExploreSelectedEntity += new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    break;
                case EntityType.Organizations:
                    entityView = new OrgNode(entity,
                        visual.EntityVisuals[EntityType.Organizations],
                        false,measure,range);
                    ((OrgNode)entityView).ExploreSelectedEntity += new OnExploreSelectedEntity(HandleExploreSelectedEntity);
                    break;
            }
            entityView.DataContext = entity;
            this.gravityGraph.Children.Add(entityView);

            entityView.MouseEnter += new MouseEventHandler(this.gravityGraph.Canvas_MouseEnter);
            entityView.MouseDown += new MouseButtonEventHandler(this.gravityGraph.Canvas_MouseDown);
            entityView.MouseMove += new MouseEventHandler(this.gravityGraph.Canvas_MouseMove);
            entityView.MouseUp += new MouseButtonEventHandler(this.gravityGraph.Canvas_MouseUp);

            return entityView;
        }

        private void HandleExploreSelectedEntity(ExploreEntityEventArgs args)
        {
            if (!string.IsNullOrEmpty(this.gravityGraph.NetworkName))
            {
                Network network = Network.Read(this.gravityGraph.NetworkName);
                RuleType ruleType = RuleType.Link;
                
                ExploreSelectedEntity explorer = new ExploreSelectedEntity(
                    network, args.SrcEntity, args.TgtEntityType);
                PopupWindow dialog = new PopupWindow();
                dialog.Content = explorer;
                dialog.Show();
            }
        }

        #region select
        private void gravityGraph_ParticleSelected(Particle particle)
        {
            if (this.EntitySelected != null)
                this.EntitySelected(particle.Tag);
        }

        public void SelectEntity(EntityBase entity)
        {
            this.gravityGraph.SelectEntity(entity);
        }

        public void SubscribeEventHandler(EntityBase entity)
        {
            this._Presenter.UpdateViewSelection(entity);
        }
        #endregion

        #region inactivate 
        
        #endregion
    }
}
